<template>
  <div class="file-upload">
    <div
      class="file-upload-container"
      v-bind="$attrs"
      @click.prevent="handleClick"
    >
      <slot name="loading" v-if="uploadStatus === 'loading'">
        <button type="button" class="btn btn-primary" disabled>
          上传中...
        </button>
      </slot>
      <slot
        name="success"
        :data="uploadData"
        v-else-if="uploadStatus === 'success'"
      >
        <button type="button" class="btn btn-primary">上传成功</button>
      </slot>
      <slot v-else>
        <button type="button" class="btn btn-primary">上传文件</button>
      </slot>
    </div>
    <input
      type="file"
      name="file"
      ref="fileInputRef"
      class="d-none"
      @change="handleChange"
    />
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, PropType, watch } from 'vue'
import axios from '@/plugins/axios'
import { TUploadStatus, TUploadBefore, IImageProp } from '@/types'
export default defineComponent({
  name: 'Uploader',
  inheritAttrs: false,
  props: {
    action: {
      type: String,
      required: true
    },
    beforeUpload: {
      type: Function as PropType<TUploadBefore>
    },
    uploaded: {
      type: Array as PropType<IImageProp[]>
    }
  },
  emits: ['on-upload-success', 'on-upload-error'],
  setup(props, { emit }) {
    const fileInputRef = ref<null | HTMLInputElement>(null)
    let uploadStatus = ref<TUploadStatus>(
      props.uploaded?.length ? 'success' : 'ready'
    )
    let uploadData = ref<IImageProp[]>(
      props.uploaded?.length ? props.uploaded : []
    )
    if (props.uploaded) {
      const uploaded = props.uploaded as IImageProp[]
      watch(
        () => [...uploaded],
        (newVal) => {
          if (newVal) {
            uploadStatus.value = 'success'
            uploadData.value = newVal
          }
        }
      )
    }

    // 点击自定义样式时触发上传控件
    const handleClick = () => {
      if (fileInputRef.value) {
        fileInputRef.value.click()
      }
    }
    // 选择文件
    const handleChange = (e: Event) => {
      const target = e.target as HTMLInputElement
      if (target.files) {
        const files = Array.from(target.files)
        if (props.beforeUpload) {
          const result = props.beforeUpload(files[0])
          if (!result) return false
        }
        uploadStatus.value = 'loading'
        const formData = new FormData()
        formData.append(target.name, files[0])
        axios
          .post(props.action, formData, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          })
          .then((res) => {
            uploadStatus.value = 'success'
            uploadData.value.push(res.data)
            emit('on-upload-success', res)
          })
          .catch((err) => {
            uploadStatus.value = 'error'
            emit('on-upload-error', err)
          })
          .finally(() => {
            target.value = ''
          })
      }
    }
    return {
      fileInputRef,
      uploadStatus,
      uploadData,
      handleClick,
      handleChange
    }
  }
})
</script>
